﻿// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.
// Modify by Fernando Cerqueira To DevPhone Toolkit

using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;


// ReSharper disable CheckNamespace
namespace DevPhone.Controls
// ReSharper restore CheckNamespace
{
    /// <summary>
    /// Represents a base class for controls that allow the user to choose a date/time.
    /// </summary>
    [TemplatePart(Name = ButtonPartName, Type = typeof(ButtonBase))]
    public class DateTimePickerBase : Control
    {
        public DateTimePickerBase()
        {
            ShowDatePart = WindowsBoxDatePickerShowPart.AllParts;
            ShowMode = DateTimePickerMode.None;
            HeaderBox = null;
        }

        private const string ButtonPartName = "DateTimeButton";

        internal WindowsBoxDatePickerShowPart ShowDatePart { get; set; }

        private ButtonBase _dateButtonPart;
        /// <summary>
        /// Event that is invoked when the Value property changes.
        /// </summary>
        public event EventHandler<DateTimeValueChangedEventArgs> ValueChanged;


        public static readonly DependencyProperty HeaderBoxProperty =
            DependencyProperty.Register("HeaderBox", typeof(string), typeof(DateTimePickerBase), new PropertyMetadata(null));

        public string HeaderBox
        {
            get { return (string)GetValue(HeaderBoxProperty); }
            set { SetValue(HeaderBoxProperty, value); }
        }

        internal DateTimePickerMode ShowMode { get; set; }

        /// <summary>
        /// Gets or sets the DateTime value.
        /// </summary>
        [TypeConverter(typeof(TimeTypeConverter))]
        [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods", Justification = "Matching the use of Value as a Picker naming convention.")]
        public DateTime? Value
        {
            get { return (DateTime?)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        /// <summary>
        /// Identifies the Value DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            "Value", typeof(DateTime?), typeof(DateTimePickerBase), new PropertyMetadata(null, OnValueChanged));

        private static void OnValueChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((DateTimePickerBase)o).OnValueChanged((DateTime?)e.OldValue, (DateTime?)e.NewValue);
        }

        private void OnValueChanged(DateTime? oldValue, DateTime? newValue)
        {
            var newdt = newValue;
            if (ShowDatePart == WindowsBoxDatePickerShowPart.DaysAndMonths)
            {
                if (newdt.HasValue)
                {
                    if (newdt.Value.Month == 2 && newdt.Value.Day == 29)
                    {
                        newdt = newdt.Value.AddDays(-1);
                        Value = newdt;
                        return;
                    }
                }
            }
            UpdateValueString();
            OnValueChanged(new DateTimeValueChangedEventArgs(oldValue, newValue));
        }

        /// <summary>
        /// Called when the value changes.
        /// </summary>
        /// <param name="e">The event d ata.</param>
        protected virtual void OnValueChanged(DateTimeValueChangedEventArgs e)
        {
            var handler = ValueChanged;
            if (null != handler)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets the string representation of the selected value.
        /// </summary>
        public string ValueString
        {
            get { return (string)GetValue(ValueStringProperty); }
            private set { SetValue(ValueStringProperty, value); }
        }

        /// <summary>
        /// Identifies the ValueString DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty ValueStringProperty = DependencyProperty.Register(
            "ValueString", typeof(string), typeof(DateTimePickerBase), null);

        /// <summary>
        /// Gets or sets the format string to use when converting the Value property to a string.
        /// </summary>
        public string ValueStringFormat
        {
            get { return (string)GetValue(ValueStringFormatProperty); }
            set { SetValue(ValueStringFormatProperty, value); }
        }

        /// <summary>
        /// Identifies the ValueStringFormat DependencyProperty.
        /// </summary>
        public static readonly DependencyProperty ValueStringFormatProperty = DependencyProperty.Register(
            "ValueStringFormat", typeof(string), typeof(DateTimePickerBase), new PropertyMetadata(null, OnValueStringFormatChanged));

        private static void OnValueStringFormatChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((DateTimePickerBase)o).OnValueStringFormatChanged(/*(string)e.OldValue, (string)e.NewValue*/);
        }

        public static readonly DependencyProperty TryTransformValueStringProperty =
            DependencyProperty.Register("TryTransformValueString", typeof(bool), typeof(DateTimePickerBase), new PropertyMetadata(true, TryTransformValueStringChangedCallback));

        private static void TryTransformValueStringChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ((DateTimePickerBase) o).OnValueStringFormatChanged();
        }

        public bool TryTransformValueString
        {
            get { return (bool)GetValue(TryTransformValueStringProperty); }
            set { SetValue(TryTransformValueStringProperty, value); }
        }

        private void OnValueStringFormatChanged(/*string oldValue, string newValue*/)
        {
            UpdateValueString();
        }

        /// <summary>
        /// Gets the fallback value for the ValueStringFormat property.
        /// </summary>
        protected virtual string ValueStringFormatFallback { get { return "{0}"; } }

        /// <summary>
        /// Called when the control's Template is expanded.
        /// </summary>
        public override void OnApplyTemplate()
        {
            // Unhook from old template
            if (null != _dateButtonPart)
            {
                _dateButtonPart.Click -= OnDateButtonClick;
            }

            base.OnApplyTemplate();

            // Hook up to new template
            _dateButtonPart = GetTemplateChild(ButtonPartName) as ButtonBase;
            if (null != _dateButtonPart)
            {
                _dateButtonPart.Click += OnDateButtonClick;
            }
        }

        private void OnDateButtonClick(object sender, RoutedEventArgs e)
        {
            OpenPickerBox();
        }

        private string TranformValueString(string fmt)
        {
            var shortdt = false;
            if (fmt == "{0:d}")
            {
                fmt = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
                shortdt = true;
            }
            if (fmt == "{0:D}")
            {
                fmt = CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern;
            }
            if (fmt == "{0:m}" || fmt == "{0:M}")
            {
                fmt = CultureInfo.CurrentCulture.DateTimeFormat.MonthDayPattern;
            }
            if (fmt == "{0:y}" || fmt == "{0:Y}")
            {
                fmt = CultureInfo.CurrentCulture.DateTimeFormat.YearMonthPattern;
            }
            var sep = string.Empty;
            if (shortdt)
            {
                var index = fmt.LastIndexOf("d", StringComparison.Ordinal) + 1;
                if (index > fmt.Length && index >= 0)
                {
                    index = fmt.LastIndexOf("M", StringComparison.Ordinal) + 1;
                }
                if (index >= 0 && index < fmt.Length)
                {
                    sep = fmt.Substring(index, 1);
                }
                switch (ShowDatePart)
                {
                    case WindowsBoxDatePickerShowPart.MonthsAndYears:
                        fmt = "Y";
                        break;
                    case WindowsBoxDatePickerShowPart.DaysAndMonthsLeapYear:
                    case WindowsBoxDatePickerShowPart.DaysAndMonths:
                        fmt = "M";
                        break;
                    case WindowsBoxDatePickerShowPart.OnlyDays31:
                    case WindowsBoxDatePickerShowPart.OnlyDays30:
                    case WindowsBoxDatePickerShowPart.OnlyDays29:
                    case WindowsBoxDatePickerShowPart.OnlyDays28:
                        fmt = fmt.Replace("M", "");
                        fmt = fmt.Replace("y", "");
                        break;
                    case WindowsBoxDatePickerShowPart.OnlyMonths:
                        fmt = fmt.Replace("d", "");
                        fmt = fmt.Replace("y", "");
                        fmt = fmt.Replace("M", "1");
                        break;
                    case WindowsBoxDatePickerShowPart.OnlyYears:
                        fmt = fmt.Replace("d", "");
                        fmt = fmt.Replace("M", "");
                        fmt = fmt.Replace("y", "2");
                        break;
                }
                if (!string.IsNullOrEmpty(sep))
                {
                    fmt = fmt.Replace(sep + sep, sep);
                    if (fmt.EndsWith(sep, StringComparison.Ordinal))
                    {
                        fmt = fmt.Substring(0, fmt.Length - 1);
                    }
                    if (fmt.StartsWith(sep, StringComparison.Ordinal))
                    {
                        fmt = fmt.Substring(1, fmt.Length - 1);
                    }
                }
                if (fmt == "d")
                {
                    fmt = "dd";
                }
                if (fmt == "1" || fmt == "11")
                {
                    fmt = "MM";
                }
                if (fmt == "22" || fmt == "2222")
                {
                    fmt = "yyyy";
                }
            }
            return "{0:" + fmt + "}";
        }

        internal void UpdateValueString()
        {
            if (ShowMode == DateTimePickerMode.None)
            {
                return;
            }
            if (!TryTransformValueString || ShowMode == DateTimePickerMode.TimePickerMode)
            {
                ValueString = string.Format(CultureInfo.CurrentCulture, ValueStringFormat ?? ValueStringFormatFallback, Value);
                return;
            }
            var fmt = ValueStringFormat ?? ValueStringFormatFallback;
            if (fmt == ValueStringFormatFallback)
            {
                ValueString = string.Format(CultureInfo.CurrentCulture, fmt, Value);
                return;
            }
            fmt = TranformValueString(fmt);
            ValueString = string.Format(CultureInfo.CurrentCulture, fmt, Value);
        }

        private void OpenPickerBox()
        {
            if (string.IsNullOrEmpty(HeaderBox))
            {
                switch (ShowMode)
                {
                    case DateTimePickerMode.DatePickerMode:
                        HeaderBox = LocalizedResources.ControlResources.DatePicker_HeaderBox;
                        break;
                    case DateTimePickerMode.TimePickerMode:
                        HeaderBox = LocalizedResources.ControlResources.TimePicker_HeaderBox;
                        break;
                    default:
                        HeaderBox = LocalizedResources.ControlResources.DatePickerBase_HeaderBox;
                        break;
                }
            }

            switch (ShowMode)
            {
                case DateTimePickerMode.DatePickerMode:
                    WindowsBoxService.DateTimePicker().DatePicker(HeaderBox, ClosePickerBox).Show(Value, ShowDatePart);
                    break;
                case DateTimePickerMode.TimePickerMode:
                    WindowsBoxService.DateTimePicker().TimePicker(HeaderBox, ClosePickerBox).Show(Value);
                    break;
            }
        }

        private void ClosePickerBox(WindowsBoxResultEventArgs e)
        {
            // Commit the value if available
            if (e.ButtonResult == WindowsBoxButtonResult.ButtonOk)
            {
                Value = ((DateTimeWrapper)e.ResultValue).DateTime;
            }
        }
    }
}
